package com.tumblr.logico2012;
import java.text.DecimalFormat;

import processing.core.PApplet;
import SimpleOpenNI.IntVector;
import SimpleOpenNI.SimpleOpenNI;
import SimpleOpenNI.SkeletonCapability;

import com.tumblr.logico2012.gestures.GestureManager;
import com.tumblr.logico2012.gestures.IGesture;
import com.tumblr.logico2012.gestures.ISwipeGesture;
import com.tumblr.logico2012.screens.IScreen;
import com.tumblr.logico2012.screens.PopupScreen;

/**
 * Test program designed to help analyse the gestures.
 */
@SuppressWarnings("serial")
public class AppletGestureAnalyser extends PApplet implements IApplet {

	public final static int SCREENWIDTH = 640;
	public final static int SCREENHEIGHT = 480;
	public final static int POSX_R_XY = SCREENWIDTH/4; // right hand, face
	public final static int POSX_R_XZ = 2*SCREENWIDTH/4; // right hand, top
	public final static int POSX_R_YZ = 3*SCREENWIDTH/4; // right hand, side
	public final static int POSY_R = SCREENHEIGHT/3; 
	public final static int POSY_R_text = 50;
	public final static int POSX_L_XY = SCREENWIDTH/4; // left hand, face
	public final static int POSX_L_XZ = 2*SCREENWIDTH/4; // left hand, top
	public final static int POSX_L_YZ = 3*SCREENWIDTH/4; // left hand, side
	public final static int POSY_L = 2*SCREENHEIGHT/3; 
	public final static int POSY_L_text = SCREENHEIGHT/2 + 50;
	public final static int LENGTH = 100;

	private GestureManager m_manager;
	private SimpleOpenNI  m_context;
	private boolean m_swipeLeft;
	private boolean m_swipeRight;

	@Override
	public IScreen getCurrentScreen() {
		return null; // we don't use any screen
	}
	
	// --------- Processing methods

	public void setup()
	{
		m_swipeLeft = m_swipeRight = false;
		
		size(SCREENWIDTH, SCREENHEIGHT,OPENGL);
		frameRate(60);
	
		//m_context = new SimpleOpenNI(this,SimpleOpenNI.RUN_MODE_MULTI_THREADED);
		m_context = new SimpleOpenNI(this);
		m_context.setMirror(true);
		m_context.enableDepth();
		m_context.enableUser(SimpleOpenNI.SKEL_PROFILE_ALL);

		m_manager = new GestureManager(this, this, m_context);

		background(0);
		stroke(255,255,255);
		fill(255,255,255);
		smooth();
	}

	public void draw()
	{
		m_context.update();
		background(0);

		m_manager.update();
		
		if(m_context.getNumberOfUsers() > 0) {
			int x, y;
			String txt;
			IntVector aUsers = new IntVector();
			m_context.getUsers(aUsers);
			// Display for the first user...
			int userId = aUsers.get(0);
			// ... for the segment right elbow to right hand ...
			GestureManager.Segment segment = m_manager.getSegment(userId, SimpleOpenNI.SKEL_RIGHT_ELBOW, SimpleOpenNI.SKEL_RIGHT_HAND);
			if(segment != null) {
				if(segment.m_hasDelta) {
					// ... Projection XY ("front view") - top-left
					stroke(255,255,255);
					x = (int)(segment.m_xy.x*LENGTH); y = (int)(segment.m_xy.y*LENGTH);
					txt = x+";"+y+"\nth = "+(new DecimalFormat("#.##").format(segment.m_angles.x));
					line(POSX_R_XY, POSY_R, POSX_R_XY+x, POSY_R+y);
					text(txt, POSX_R_XY - textWidth(txt)/2, POSY_R_text);
					
					// Projection XZ ("top view") - top-middle
					x = (int)(segment.m_xz.x*LENGTH); y = (int)(segment.m_xz.y*LENGTH);
					txt = x+";"+y+"\nth = "+(new DecimalFormat("#.##").format(segment.m_angles.y)); 
					line(POSX_R_XZ, POSY_R, POSX_R_XZ+x, POSY_R+y);
					text(txt, POSX_R_XZ - textWidth(txt)/2, POSY_R_text);

					// Projection YZ ("side view") - top-right
					x = (int)(segment.m_yz.x*LENGTH); y = (int)(segment.m_yz.y*LENGTH);
					txt = x+";"+y+"\nth = "+(new DecimalFormat("#.##").format(segment.m_angles.z)); 
					line(POSX_R_YZ, POSY_R, POSX_R_YZ+x, POSY_R+y);
					text(txt, POSX_R_YZ - textWidth(txt)/2, POSY_R_text);
	
					// For each projection, in red: delta of the angle between the vector and the reference axis
					stroke(255,0,0);
					line(POSX_R_XY, POSY_R, POSX_R_XY+LENGTH*(float)Math.cos(segment.m_delta_angles.x), POSY_R+LENGTH*(float)Math.sin(segment.m_delta_angles.x));
					line(POSX_R_XZ, POSY_R, POSX_R_XZ+LENGTH*(float)Math.cos(segment.m_delta_angles.y), POSY_R+LENGTH*(float)Math.sin(segment.m_delta_angles.y));
					line(POSX_R_YZ, POSY_R, POSX_R_YZ+LENGTH*(float)Math.cos(segment.m_delta_angles.z), POSY_R+LENGTH*(float)Math.sin(segment.m_delta_angles.z));
	
					// Thresholds
					stroke(0,0,255);
					line(POSX_R_XY, POSY_R, POSX_R_XY+LENGTH*(float)Math.cos(ISwipeGesture.START_THRESHOLD_ANGLE_SPEED_XY), POSY_R+LENGTH*(float)Math.sin(ISwipeGesture.START_THRESHOLD_ANGLE_SPEED_XY));
					line(POSX_R_XZ, POSY_R, POSX_R_XZ+LENGTH*(float)Math.cos(ISwipeGesture.START_THRESHOLD_ANGLE_SPEED_XZ), POSY_R+LENGTH*(float)Math.sin(ISwipeGesture.START_THRESHOLD_ANGLE_SPEED_XZ));
					line(POSX_R_YZ, POSY_R, POSX_R_YZ+LENGTH*(float)Math.cos(ISwipeGesture.THRESHOLD_RANGE_ANGLE_SPEED_YZ[0]), POSY_R+LENGTH*(float)Math.sin(ISwipeGesture.THRESHOLD_RANGE_ANGLE_SPEED_YZ[0]));
	
					line(POSX_R_XY, POSY_R, POSX_R_XY+LENGTH*(float)Math.cos(ISwipeGesture.STOP_THRESHOLD_ANGLE_SPEED_XY), POSY_R+LENGTH*(float)Math.sin(ISwipeGesture.STOP_THRESHOLD_ANGLE_SPEED_XY));
					line(POSX_R_XZ, POSY_R, POSX_R_XZ+LENGTH*(float)Math.cos(ISwipeGesture.STOP_THRESHOLD_ANGLE_SPEED_XZ), POSY_R+LENGTH*(float)Math.sin(ISwipeGesture.STOP_THRESHOLD_ANGLE_SPEED_XZ));
					line(POSX_R_YZ, POSY_R, POSX_R_YZ+LENGTH*(float)Math.cos(ISwipeGesture.THRESHOLD_RANGE_ANGLE_SPEED_YZ[1]), POSY_R+LENGTH*(float)Math.sin(ISwipeGesture.THRESHOLD_RANGE_ANGLE_SPEED_YZ[1]));
				}
			}

			// ... for the segment left elbow to left hand ...
			segment = m_manager.getSegment(userId, SimpleOpenNI.SKEL_LEFT_ELBOW, SimpleOpenNI.SKEL_LEFT_HAND);
			if(segment != null) {
				if(segment.m_hasDelta) {
					// ... Projection XY ("front view") - bottom-left
					stroke(255,255,255);
					x = (int)(segment.m_xy.x*LENGTH); y = (int)(segment.m_xy.y*LENGTH);
					txt = x+";"+y+"\nth = "+(new DecimalFormat("#.##").format(segment.m_angles.x));
					line(POSX_L_XY, POSY_L, POSX_L_XY+x, POSY_L+y);
					text(txt, POSX_L_XY - textWidth(txt)/2, POSY_L_text);
					
					// Projection XZ ("top view") - bottom-middle
					x = (int)(segment.m_xz.x*LENGTH); y = (int)(segment.m_xz.y*LENGTH);
					txt = x+";"+y+"\nth = "+(new DecimalFormat("#.##").format(segment.m_angles.y)); 
					line(POSX_L_XZ, POSY_L, POSX_L_XZ+x, POSY_L+y);
					text(txt, POSX_L_XZ - textWidth(txt)/2, POSY_L_text);

					// Projection YZ ("side view") - bottom-right
					x = (int)(segment.m_yz.x*LENGTH); y = (int)(segment.m_yz.y*LENGTH);
					txt = x+";"+y+"\nth = "+(new DecimalFormat("#.##").format(segment.m_angles.z)); 
					line(POSX_L_YZ, POSY_L, POSX_L_YZ+x, POSY_L+y);
					text(txt, POSX_L_YZ - textWidth(txt)/2, POSY_L_text);
	
					// For each projection, in red: delta of the angle between the vector and the reference axis
					stroke(255,0,0);
					line(POSX_L_XY, POSY_L, POSX_L_XY+LENGTH*(float)Math.cos(segment.m_delta_angles.x), POSY_L+LENGTH*(float)Math.sin(segment.m_delta_angles.x));
					line(POSX_L_XZ, POSY_L, POSX_L_XZ+LENGTH*(float)Math.cos(segment.m_delta_angles.y), POSY_L+LENGTH*(float)Math.sin(segment.m_delta_angles.y));
					line(POSX_L_YZ, POSY_L, POSX_L_YZ+LENGTH*(float)Math.cos(segment.m_delta_angles.z), POSY_L+LENGTH*(float)Math.sin(segment.m_delta_angles.z));
					
					// Thresholds
					stroke(0,0,255);
					line(POSX_L_XY, POSY_L, POSX_L_XY+LENGTH*(float)Math.cos(ISwipeGesture.START_THRESHOLD_ANGLE_SPEED_XY), POSY_L+LENGTH*(float)Math.sin(ISwipeGesture.START_THRESHOLD_ANGLE_SPEED_XY));
					line(POSX_L_XZ, POSY_L, POSX_L_XZ+LENGTH*(float)Math.cos(ISwipeGesture.START_THRESHOLD_ANGLE_SPEED_XZ), POSY_L+LENGTH*(float)Math.sin(ISwipeGesture.START_THRESHOLD_ANGLE_SPEED_XZ));
					line(POSX_L_YZ, POSY_L, POSX_L_YZ+LENGTH*(float)Math.cos(ISwipeGesture.THRESHOLD_RANGE_ANGLE_SPEED_YZ[0]), POSY_L+LENGTH*(float)Math.sin(ISwipeGesture.THRESHOLD_RANGE_ANGLE_SPEED_YZ[0]));
	
					line(POSX_L_XY, POSY_L, POSX_L_XY+LENGTH*(float)Math.cos(ISwipeGesture.STOP_THRESHOLD_ANGLE_SPEED_XY), POSY_L+LENGTH*(float)Math.sin(ISwipeGesture.STOP_THRESHOLD_ANGLE_SPEED_XY));
					line(POSX_L_XZ, POSY_L, POSX_L_XZ+LENGTH*(float)Math.cos(ISwipeGesture.STOP_THRESHOLD_ANGLE_SPEED_XZ), POSY_L+LENGTH*(float)Math.sin(ISwipeGesture.STOP_THRESHOLD_ANGLE_SPEED_XZ));
					line(POSX_L_YZ, POSY_L, POSX_L_YZ+LENGTH*(float)Math.cos(ISwipeGesture.THRESHOLD_RANGE_ANGLE_SPEED_YZ[1]), POSY_L+LENGTH*(float)Math.sin(ISwipeGesture.THRESHOLD_RANGE_ANGLE_SPEED_YZ[1]));
				}
			}
		}
		
		// show ellipse in the corners when swipe gestures are being performed
		if(m_swipeRight) {
			stroke(255,0,0);
			fill(255,0,0);
			ellipse(50,50,10,10);
			fill(255,255,255);
		}
		if(m_swipeLeft) {
			stroke(255,0,0);
			fill(255,0,0);
			ellipse(SCREENWIDTH-50,50,10,10);
			fill(255,255,255);
		}
	}

	// -----------------------------------------------------------------
	// SimpleOpenNI events

	public void onNewUser(int userId)
	{
	  println("onNewUser - userId: " + userId);

	  SkeletonCapability cap = m_context.getUserGenerator().GetSkeletonCap();
	  if(cap.NeedPoseForCalibration()) {
		  // automatic calibration not supported - we use a pose -> onStartPose
		  println("  start pose detection");
		  m_context.startPoseDetection("Psi",userId);
	  } else {
		  // we try automatic calibration - if it fails -> onEndCalibration with successful == false 
		  println("  calibration without pose");
		  cap.RequestCalibration(userId, true);
	  }
	}

	public void onLostUser(int userId)
	{
		// the user has been lost
		println("onLostUser - userId: " + userId);
		m_manager.unregisterUser(userId);
	}

	public void onStartPose(String pose,int userId)
	{
		// the pose has been identified. We stop and calibrate.
	  println("onStartPose - userId: " + userId + ", pose: " + pose);
	  println(" stop pose detection");
	  
	  m_context.stopPoseDetection(userId); 
	  m_context.requestCalibrationSkeleton(userId, true);
	}

	public void onEndPose(String pose,int userId)
	{
	  println("onEndPose - userId: " + userId + ", pose: " + pose);
	}

	public void onStartCalibration(int userId)
	{
	  println("onStartCalibration - userId: " + userId);
	}

	public void onEndCalibration(int userId, boolean successfull)
	{
	  println("onEndCalibration - userId: " + userId + ", successfull: " + successfull);
	  
	  if (successfull) 
	  { 
	    println("  User calibrated !!!");
	    m_context.startTrackingSkeleton(userId); 
	    
	    // swipe right with right hand
	    m_manager.addGesture(new ISwipeGesture(userId, IGesture.RIGHT, IGesture.RIGHT, AppletGestureAnalyser.this, "testicon.jpeg") {
			@Override
			public void gestureAborted(GestureManager manager) {
				println("swipe right aborted!");
				m_swipeRight = false;
			}

			@Override
			public void gestureEnded(GestureManager manager) {
				println("swipe right ended!");
				m_swipeRight = false;				
			}

			@Override
			public void gestureStarted(GestureManager manager) {
				m_swipeRight = true;
			}
	    });
	    // swipe right with left hand
	    m_manager.addGesture(new ISwipeGesture(userId, IGesture.LEFT, IGesture.RIGHT, AppletGestureAnalyser.this, "testicon.jpeg") {
			@Override
			public void gestureAborted(GestureManager manager) {
				println("swipe right aborted!");
				m_swipeRight = false;
			}

			@Override
			public void gestureEnded(GestureManager manager) {
				println("swipe right ended!");
				m_swipeRight = false;
			}

			@Override
			public void gestureStarted(GestureManager manager) {
				m_swipeRight = true;
			}
	    });
	    // swipe left with right hand
	    m_manager.addGesture(new ISwipeGesture(userId, IGesture.RIGHT, IGesture.LEFT, AppletGestureAnalyser.this, "testicon.jpeg") {
			@Override
			public void gestureAborted(GestureManager manager) {
				println("swipe left aborted!");
				m_swipeLeft = false;
			}

			@Override
			public void gestureEnded(GestureManager manager) {
				println("swipe left ended!");
				m_swipeLeft = false;
			}

			@Override
			public void gestureStarted(GestureManager manager) {
				m_swipeLeft = true;
			}
	    });
	    // swipe left with left hand
	    m_manager.addGesture(new ISwipeGesture(userId, IGesture.LEFT, IGesture.LEFT, AppletGestureAnalyser.this, "testicon.jpeg") {
			@Override
			public void gestureAborted(GestureManager manager) {
				println("swipe left aborted!");
				m_swipeLeft = false;
			}

			@Override
			public void gestureEnded(GestureManager manager) {
				println("swipe left ended!");
				m_swipeLeft = false;
			}

			@Override
			public void gestureStarted(GestureManager manager) {
				m_swipeLeft = true;
			}
	    });
	  } 
	  else 
	  { 
		  // (automatic) calibration failed, try with pose 
	    println("  Failed to calibrate user !!!");
	    println("  Start pose detection");
	    m_context.startPoseDetection("Psi",userId);
	  }
	}

	@Override
	public void addPopup(PopupScreen popup) {
	}
}
